package com.mrd.util;

import com.mrd.annotation.freemarker.MRDFreeMarkerCustomFields;
import com.mrd.gtimp.business.entity.Declaration;

import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by dengdc on 2016/10/21.
 */
public class FreeMarkerUtils {
    /**
     * 因为freemaker的特殊性，在jtl中取出null和空字符串时会报错，所以需要将null消除
     * @param t
     */
    /**
     * {declaration:[]}
     */
    public static void normalFieldValue(Object t) {
        Class tClass = t.getClass();
        Field[] fields = tClass.getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            setNormalFieldDefaultValue(t, field);
        }
    }

    public static void prepareUseForFTL(Object t) {
        long begin = System.currentTimeMillis();
        Class tClass = t.getClass();
        MRDFreeMarkerCustomFields makerFillUp = (MRDFreeMarkerCustomFields) tClass
                .getAnnotation(MRDFreeMarkerCustomFields.class);
        if (makerFillUp != null) {
            String[] supers = makerFillUp.superCustomFields();
            String[] customs = makerFillUp.customClassFields();
            superFieldValue(t, supers);
            customClassFieldValue(t, customs);
        }
        normalFieldValue(t);
        long finish = System.currentTimeMillis();
        System.out.println("FreeMarkerUtils.prepareUseForFTL()耗时(单位/ms): " + (finish - begin));
    }

    /**
     * @param t
     * @param superFieldNames
     */
    private static void superFieldValue(Object t, String[] superFieldNames) {
        Class sClass = t.getClass().getSuperclass();
        Field[] fields = sClass.getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            setNormalFieldDefaultValue(t, field);
        }
        superCustomClassFieldValue(t, superFieldNames);
    }

    /**
     * 设置自定义类型的默认值
     */
    private static void customClassFieldValue(Object t, String[] fieldNames) {
        Class tClass = t.getClass();
        for (String fieldName : fieldNames) {
            setCustomFiledDefaultValue(t, tClass, fieldName);
        }
    }

    /**
     * 设置父类型属性的默认值
     */
    private static void superCustomClassFieldValue(Object t, String[] fieldNames) {
        Class superClass = t.getClass().getSuperclass();
        for (String fieldName : fieldNames) {
            setCustomFiledDefaultValue(t, superClass, fieldName);
        }
    }

    /**
     * 设置field的值
     *
     * @param t    对象
     * @param name fieldName
     */
    private static void setCustomFiledDefaultValue(Object t, Class clazz, String name) {
        try {
            Field field = clazz.getDeclaredField(name);
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, name);
            Method readMethod = pd.getReadMethod();
            Object val = readMethod.invoke(t);
            if (val != null) {
                if (field.getType().isAssignableFrom(List.class)) {
                    List list = (List) val;
                    for (Object o : list) {
                        normalFieldValue(o);
                    }
                } else if (field.getType().isAssignableFrom(Set.class)) {
                    Set set = (Set) val;
                    for (Object o : set) {
                        normalFieldValue(o);
                    }
                } else if (field.getType().isAssignableFrom(Object[].class)) {
                    Object[] arr = (Object[]) val;
                    for (Object o : arr) {
                        normalFieldValue(o);
                    }
                } else {
                    normalFieldValue(val);
                }
            } else {
                // 为空自定义类型的属性设置初值，并为其子属性设置初值
                if (field.getType().isAssignableFrom(List.class)
                        || field.getType().isAssignableFrom(Set.class)
                        || field.getType().isAssignableFrom(Object[].class)) {
                } else {
                    Method writeMethod = pd.getWriteMethod();
                    if (writeMethod != null) {
                        Object newInstance = field.getType().newInstance();
                        normalFieldValue(newInstance);
                        writeMethod.invoke(t, newInstance);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void setNormalFieldDefaultValue(Object t, Field field) {
        PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(t.getClass(), field.getName());
        if (pd == null) {
            return;
        }
        Method readMethod = pd.getReadMethod();
        Class fieldClass = field.getType();
        try {
            if (readMethod != null) {
                Object val = readMethod.invoke(t);
                if (val == null) {
                    Method writeMethod = pd.getWriteMethod();
                    // 是否是基本类型
                    if (fieldClass.isPrimitive() || isBaozhuangLei(fieldClass)) {
                        setBasicClassValue(t, fieldClass, writeMethod);
                    } else {
                        setOtherClassValue(t, fieldClass, writeMethod);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(field);
            e.printStackTrace();
        }
    }

    /**
     * 给field设置一个默认值， field为基本类型或是基本类型的包装类
     */
    private static void setBasicClassValue(Object t, Class fieldClass, Method writeMethod)
            throws Exception {
        if (fieldClass.equals(Integer.class) || fieldClass.equals(Long.class)
                || fieldClass.equals(Short.class) || fieldClass.equals(Byte.class)
                || fieldClass.equals(Character.class)) {
            writeMethod.invoke(t, 0);
        } else if (fieldClass.equals(Float.class) || fieldClass.equals(Double.class)) {
            writeMethod.invoke(t, 0.0);
        } else if (fieldClass.equals(Boolean.class)) {
            writeMethod.invoke(t, false);
        }
    }

    /**
     * 判断是否是基本类型的包装类
     */
    private static boolean isBaozhuangLei(Class clazz) {
        return clazz.equals(Integer.class) || clazz.equals(Long.class) || clazz.equals(Short.class)
                || clazz.equals(Byte.class) || clazz.equals(Character.class)
                || clazz.equals(Float.class) || clazz.equals(Double.class)
                || clazz.equals(Boolean.class);
    }

    private static void setOtherClassValue(Object t, Class fieldClass, Method writeMethod)
            throws Exception {
        if (fieldClass.isAssignableFrom(List.class)) {
            writeMethod.invoke(t, new ArrayList<>());
        } else if (fieldClass.isAssignableFrom(Set.class)) {
            writeMethod.invoke(t, new HashSet<>());
        } else if (fieldClass.isAssignableFrom(Object[].class)) {
            writeMethod.invoke(t, new Object[1]);
        } else if (fieldClass.isAssignableFrom(Map.class)) {
            writeMethod.invoke(t, new HashMap<>());
        } else {
            if (writeMethod != null) {
                Class[] parameterTypes = writeMethod.getParameterTypes();
                writeMethod.invoke(t, parameterTypes[0].newInstance());
            }
        }
    }

    public static void main(String[] args) {
        Declaration declaration = new Declaration();
        normalFieldValue(declaration);
    }

    /**
     * 根据ftl模板生成xml
     *
     * @param directory   模板文件所在目录
     * @param ftlName     模板文件名
     * @param params      模板中的参数
     * @param xmlFilePath 生成文件的位置及文件名称
     */
    public static void toXML(String directory, String ftlName, Map<String, Object> params, String xmlFilePath) {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
        configuration.setDefaultEncoding("UTF-8");
        Template t = null;
        try {
            configuration.setTemplateLoader(new FileTemplateLoader(new File(directory)));
            t = configuration.getTemplate(ftlName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        File outFile = new File(xmlFilePath);
        Writer out = null;
        try {
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        try {
            t.process(params, out);
        } catch (TemplateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    out = null;
                }
            }
        }
    }
}
